---
id: index
title: vue
---
### Vue.js中的响应式系统使用了观察者模式而非发布订阅者模式。
观察者模式是指对象间的一种一对多的依赖关系，当一个对象的状态发生改变时，所有依赖它的对象都将得到通知并自动更新。在Vue.js中，数据对象被视为被观察的对象，而观察者则是视图上的依赖。当数据对象发生变化时，观察者就会收到通知并且更新视图。

与观察者模式不同的是，发布订阅者模式中，发布者与订阅者之间的关系是一对多的，发布者发布消息后，所有订阅者都会收到这个消息，但订阅者之间并没有依赖关系，订阅者也可以选择只接收自己感兴趣的消息。

在Vue.js中，每个组件实例都有一个对应的观察者对象，当组件的数据发生变化时，这个观察者对象就会通知对应的依赖更新视图。Vue.js的响应式系统是基于这个观察者模式来实现的。

### Vue、React、angularjs、jquery 的区别

    - JQuery 与另外几者最大的区别是 JQuery 是事件驱动，其他两者是数据驱动
    - JQuery 业务逻辑和UI更改混在一起，让本来混沌的逻辑更加混乱
    - Angular，Vue 是双向绑定，而 React 不是

### Vue 和 JQuery 的区别在哪？为什么放弃 JQuery 用 Vue？

    - JQuery 是直接操作 DOM改变视图，Vue 操作数据改变视图。
    - 在操作 DOM 频繁的场景里，JQuery 的操作 DOM 行为是频繁的，而 Vue 利用虚拟 DOM 的技术，大大提高了更新 DOM 时的性能
    - Vue 中不提倡直接操作 DOM，开发者只需要把大部分精力放在 数据层面上
    - Vue 集成的一些库，大大提高开发效率，比如 Vuex，Router 等

### vue 有哪些生命周期

- vue2
  - beforeCreate
  - created
  - beforeMount
  - mounted
  - beforeUpdate
  - updated
  - beforeDestroy
  - destroyed
- vue3
  - beforeCreate
  - created
  - beforeMount
  - onMounted
  - beforeUpdate
  - updated
  - beforeUnmount
  - unmounted

### 进入 vue 页面或组件，生命周期的执行顺序

- beforeCreate
- created
- beforeMount
- mounted

### Vue 父子组件的生命周期顺序

- 加载渲染过程： 父 beforeCreate->父 created->父 beforeMount->子 beforeCreate->子 created->子 beforeMount->子 mounted->父 mounted
- 子组件更新过程：父 beforeUpdate->子 beforeUpdate->子 updated->父 updated
- 父组件更新过程：父 beforeUpdate->父 updated
- 销毁过程： 父 beforeDestroy->子 beforeDestroy->子 destroyed->父 destroyed

### 在哪个阶段有$el，在哪个阶段有$data

1.  beforeCreate 啥也没有
2.  created 有 data
3.  beforeMount 有 data 没有 el
4.  mounted 都有

### 什么阶段（生命周期）才能访问操作 dom？为什么

在钩子函数 mounted()中才能开始访问操作 dom，因为在 mounted()生命周期前，dom 刚好渲染好，但还未挂载到页面，如果在这之前进行 dom 操作，将找不到 dom 节点

### Vue 中 watch 用法详解

在 vue 中，使用 watch 来监听数据的变化；

- 监听的数据后面可以写成对象形式，包含 handler 方法，immediate 和 deep。
- immediate 表示在 watch 中首次绑定的时候，是否执行 handler，值为 true 则表示在 watch 中声明的时候，就立即执行 handler 方法，值为 false，则和一般使用 watch 一样，在数据发生变化的时候才执行 handler。
- 当需要监听一个对象的改变时，普通的 watch 方法无法监听到对象内部属性的改变，只有 data 中的数据才能够监听到变化，此时就需要 deep 属性对对象进行深度监听。

### vue 中对 mixins 的理解和使用

- mixins 是一种分发 Vue 组件中可复用功能的非常灵活的方式。混合对象可以包含任意组件选项。当组件使用混合对象时，所有混合对象的选项将被混入该组件本身的选项。
- 而 mixins 引入组件之后，则是将组件内部的内容如 data 等方法、method 等属性与父组件相应内容进行合并。相当于在引入后，父组件的各种属性方法都被扩充了。
- 可点击 vue 中对 mixins 的理解和使用的介绍作为参考

### 为什么 vue 采用异步渲染和异步更新机制是如何实现的？

因为如果不采用异步更新，那么每次更新数据都会对当前组件进行重新渲染；所以为了性能考虑，Vue 会在本轮数据更新后，再去异步更新视图。

- 原理：

  - 调用 notify() 方法，通知 watcher 进行更新操作
  - 依次调用 watcher 的 update 方法
  - 对 watcher 进行去重操作（通过 id），放到队列里
  - 执行完后异步清空这个队列， nextTick(flushSchedulerQueue) 进行批量更新操作

- 异步更新机制的实现:
  - Vue 的异步更新机制的核心是利用了浏览器的异步任务队列来实现的，首选微任务队列，宏任务队列次之。
  - 当响应式数据更新后，会调用 dep.notify 方法，通知 dep 中收集的 watcher 去执行 update 方法，watcher.update 将 watcher 自己放入一个 watcher 队列（全局的 queue 数组）。
  - 然后通过 nextTick 方法将一个刷新 watcher 队列的方法（flushSchedulerQueue）放入一个全局的 callbacks 数组中。
  - 如果此时浏览器的异步任务队列中没有一个叫 flushCallbacks 的函数，则执行 timerFunc 函数，将 flushCallbacks 函数放入异步任务队列。如果异步任务队列中已经存在 flushCallbacks 函数，等待其执行完成以后再放入下一个 flushCallbacks 函数。
  - flushCallbacks 函数负责执行 callbacks 数组中的所有 flushSchedulerQueue 函数。
  - flushSchedulerQueue 函数负责刷新 watcher 队列，即执行 queue 数组中每一个 watcher 的 run 方法，从而进入更新阶段，比如执行组件更新函数或者执行用户 watch 的回调函数。

### vue 为什么在 HTML 中监听事件？

你可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 这个长期以来的优良传统。但不必担心，因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上，它不会导致任何维护上的困难。实际上，使用 v-on 或 @ 有几个好处：

- 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
- 因为你无须在 JavaScript 里手动绑定事件，你的 ViewModel 代码可以是非常纯粹的逻辑，和 DOM 完全解耦，更易于测试。
- 当一个 ViewModel 被销毁时，所有的事件处理器都会自动被删除。你无须担心如何清理它们

### vm.$set(obj, key, val) 做了什么？

在一个组件实例中，只有在 data 里初始化的数据才是响应的，Vue 不能检测到对象属性的添加或删除，没有在 data 里声明的属性不是响应的,所以数据改变了但是不会在页面渲染； vm.set 用于向响应式对象添加一个新的 property，并确保这个新的 property 同样是响应式的，并触发视图更新。

- 解决办法：
  - 使用 Vue.set(object, key, value) 方法将响应属性添加到嵌套的对象上
  - 为对象添加一个新的响应式数据：调用 defineReactive 方法为对象增加响应式数据，然后执行 dep.notify 进行依赖通知，更新视图

### 组件内的导航钩子

组件内的导航钩子主要有这三种：beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。他们是直接在路由组件内部直接进行定义的

### vue 和 react 区别

- 相同：

  - 都使用 vdom；
  - 都提供了响应式和组件化的视图组件；
  - 核心都在 UI 层（比如路由，状态管理，数据请求交给其他库）；
  - 都是单向数据流；
  - 轻量级

- 不同：
  - vue 基于 html 的模板语法，react 是 jsx 语法，可以 html 和 js 混写；
  - 状态管理 vue 是 data，react 是 state 但是不能直接修改 state，需要 setState 方法；
  - vue 使用 slot 插槽进行嵌套传递，react 使用 props.children 传递；
  - vue 使用指令渲染，react 逻辑运算符；
  - 父子组件传值，vue 使用 props 和 $emit，react 使用 props 和子组件触发父组件的方法，父组件通过 setState 修改；
  - 路由，vue 路由组件都会渲染到 router-view 里面，react 是全局组件的方式，子组件作为 children 传递到父组件；
  - vue 实现双向绑定，react 没有；
  - vue 父组件更新子组件不会动，react 父更新子必更新，需要手动设置；

### template 模板引擎的渲染过程

template 的作用是模板占位符，可帮助我们包裹元素，但在循环过程当中，template 不会被渲染到页面上

在 vue 实例初始化 $mount 的时候，先调用 render 函数如果 render 函数不存在，则调用 template 进行编译得到 render 函数。如果没有 template 则会调用 el 来获取 template。
渲染过程：

- 获取模板，并将模板通过 compile 编译器（通过正则等方式解析 template 里面的指令，class、style 等）编译成 AST 语法树；
- 把得到的 AST 通过 generate 转化成 render 渲染函数，render 函数的返回值是 vdom；
- vue 构造函数直接使用 render 渲染函数渲染 dom；

### 组件中写 name 选项有哪些好处

1、可以通过名字找到对应的组件（ 递归组件：组件自身调用自身 ）  
2、可以通过 name 属性实现缓存功能 (keep-alive)  
3、可以通过 name 来识别组件（跨级组件通信时非常重要）  
4、使用 vue-devtools 调试工具里显示的组见名称是由 vue 中组件 name 决定的

### 子组件里面可以修改父组件的值吗

传递的是对象和数组可以修改，如果是基础数据类型也可以修改，但是控制台会报错；  
对象和数组都是引用类型，父组件传递过来的是一个地址，子组件修改的是地址里面的内容，地址本身并没有变，所以不会报错，但是基础数据类型就不同了，他是直接修改了传递的值，但是 vue 不允许在子组件里面直接修改 prop 值，所以就会报错。

- 那么基础类型要怎么修改呢？
  - 可以使用.sync 修饰符来修改(vue3 中 已移除)。
  - 子组件用 data 或者 computed 重新定义一个变量，然后修改新定义的变量。

### vue 是怎么检测数组的变化的

- 使用函数劫持的方式，重写了数组的方法（push、pop、shift、unshift、sort、reverse、splice）
- 无响应式的方法：filter() concat() slice() map() 新数组替换旧数组 不会改变原数组，页面不更新 不会被拦截
- 流程:
  - 初始化传入 data 数据执行 initData
  - 将数据进行观测 new Observer
  - 将数组原型方法指向重写的原型
  - 深度观察数组中的引用类型

### vue 组件渲染和更新的过程

- 渲染：

  - 把模板解析为 render 函数
  - 触发响应式，监听 data 属性 getter setter
  - 执行 render 函数，生成 vnode，patch（elem，vnode）

- 更新：
  - 修改 data，触发 setter（此前在 getter 中已被监听）
  - 重新执行 render 函数，生成 newVnode
  - patch（vnode，newVnode）

### vue 为什么要使用异步组件

组件功能多打包出的结果会变大，我可以采用异步的方式来加载组件。  
异步组件的核心是把组件定义成一个函数，主要依赖 import()这个语法，可以实现文件的分割加载。

原理：

- 在 createComponent 方法中，会有相应的异步组件处理
- 首先定义一个 asyncFactory 变量，然后进行判断，如果组件是一个函数，然后会去调 resolveAsyncComponent 方法；
- 然后将赋值在 asyncFactory 上的函数传进去，会让 asyncFactory 马上执行，执行的时候并不会马上返回结果，因为他是异步的，返回的是一个 promise，这时候这个值就是 undefined，然后就会先渲染一个异步组件的占位，空虚拟节点。
- 如果加载完之后会调 factory 函数传入 resolve 和 reject 两个参数，执行后返回一个成功的回调和失败的回调，promise 成功了就会调 resolve；
- resolve 中就会调取 forceRender 方法强制更新视图重新渲染，forceRender 中调取的就是$forceUpdate，同时把结果放 factory.resolved 上；
- 如果强制刷新的时候就会再次走 resolveAsyncComponent 方法，这时候有个判断，如果有成功的结果就把结果直接放回去，这时候 resolveAsyncComponent 返回的就不是 undefined 了，就会接的创建组件，初始化组件，渲染组件。

### vue 如何快速定位那个组件出现性能问题的

⽤ timeline ⼯具。 通过 timeline 来查看每个函数的调⽤时常，定位出哪个函数的问题，从⽽能判断哪个组件出了问题

### ref 是什么？

- vue2 中是用来获取 dom 的
- vue3 中是响应式函数

### 组件中的 data 为什么是个函数？

- 组件是用来复用的，js 里对象是引用关系，如果是对象形式，那么 data 的作用域是没有隔离的，在多个子组件时，会被外部因素影响。
- 如果 data 是一个函数，那么每个实例可以独自拥有一份返回对象的拷贝，组件实例之间的 data 属性值不会互相影响

### 对 MVVM 的理解？

MVVM 是一种前端架构模式，其全称是“Model-View-ViewModel”。

- MVVM 的基本思想是将前端应用程序分为三个部分：
  - 模型（Model）：代表应用程序的数据和业务逻辑，通常是通过 Ajax 请求从服务器获取数据。
  - 视图（View）：负责应用程序的用户界面，通常是 HTML、CSS 和 JavaScript 代码的组合。
  - 视图模型（ViewModel）：作为模型和视图之间的中介，它负责将模型中的数据转换为视图所需的格式，并处理视图中的用户交互事件。
- MVVM 模式的优点是：
  - 解耦合：通过将模型、视图和视图模型分开，可以更好地管理代码，使其更易于扩展和维护。
  - 数据绑定：MVVM 模式中的双向数据绑定机制可以实现视图和模型之间的同步，减少了编写重复代码的需求。
  - 可测试性：MVVM 模式使应用程序更易于测试，因为它将业务逻辑和用户界面分开，并提供了视图模型的抽象层。
    一些常见的 MVVM 框架包括：Vue.js、AngularJS、Knockout 等。这些框架使用不同的方法实现 MVVM 模式，但它们的目标都是为了提高应用程序的可维护性、可扩展性和可测试性。

### Vue 修饰符有哪些？

- 事件修饰符：

  - .stop 阻止事件继续传播
  - .prevent 阻止标签默认行为
  - .capture 使用事件捕获模式，即元素自身触发的事件先在此处处理，然后才交由内部元素进行处理
  - .self 只当在 event.target 是当前元素自身时触发处理函数
  - .once 事件将只会触发一次
  - .passive 告诉浏览器你不想阻止事件的默认行为

- v-model 的修饰符：

  - .lazy 通过这个修饰符，转变为在 change 事件再同步
  - .number 将自动过滤用户的输入值转化为数值类型
  - .trim 自动过滤用户输入的首位空格

- 键盘事件的修饰符：
  - .enter 回车
  - .tab
  - .delete 删除或回退
  - .esc
  - .space 空格
  - .up 上键
  - .down 下键
  - .left 左键
  - .right 右键
- 系统修饰符：
  - .ctrl
  - .alt
  - .shift
  - .meta
- 鼠标按钮修饰符：
  - .left
  - .right
  - .middle

### 函数式组件与普通组件的区别：

函数式组件需要在声明组件是指定 functional：true

```js
// （2.5版本后）
<template functional>
  <button class="btn btn-primary" v-bind="data.attrs" v-on="listeners">
    <slot />
  </button>
</template>;

// 或者

Vue.component("my-component", {
  functional: true,
  // Props 是可选的
  props: {
    // ...
  },
  // 为了弥补缺少的实例
  // 提供第二个参数作为上下文
  render: function (createElement, context) {
    // ...
  },
});
```

- 区别
  - 不需要实例化，所以没有 this
  - 没有声明周期钩子函数，不能使用计算属性，watch
  - 不能通过$emit 对外暴露事件，调用事件只能通过 context.listeners.click 的方式调用外部传入的事件
  - 因为函数式组件是没有实例化的，所以在外部通过 ref 去引用组件时，实际引用的是 HTMLElement
  - 函数式组件的 props 可以不用显示声明，所以没有在 props 里面声明的属性都会被自动隐式解析为 prop,而普通组件所有未声明的属性都解析到 $attrs 里面，并自动挂载到组件根元素上面(可以通过 inheritAttrs 属性禁止)
- 优点：
  - 由于函数组件不需要实例化，无状态，没有生命周期，所以渲染性能要好于普通组件
  - 函数式组件结构比较简单，代码结构更清晰
- 使用场景：
  - 一个简单的展示组件，作为容器组件使用 比如 router-view 就是一个函数式组件
  - 高阶组件---用于接收一个组件作为参数，返回一个被包装过的组件

### Vue 组件通信有哪些方式

- 父传子：props
  - 父组件通过 props 向下传递数据给子组件。注：组件中的数据共有三种形式：data、props、computed
- 父传子孙：provide 和 inject
  - 父组件定义 provide 方法 return 需要分享给子孙组件的属性，子孙组件使用 inject 选项来接收指定的我们想要添加在这个实例上的 属性；
- 子传父：通过事件形式
  - 子组件通过 $emit()给父组件发送消息，父组件通过 v-on 绑定事件接收数据。
- 父子、兄弟、跨级：eventBus.js
  - 这种方法通过一个空的 Vue 实例作为中央事件总线（事件中心）,用它来（emit ） 触 发 事 件 和 （ emit）触发事件和（emit）触发事件和（on）监听事件，巧妙而轻量地实现了任何组件间的通信。
- 通信插件：PubSub.js
- vuex

### provide 和 inject 使用方法

```js
// 祖先组件中声明provide选项
export default {
  provide: {
    message: "Hello from the ancestor component",
  },
  // ...
};
```

```js
// 后代组件中使用inject选项引入provide中声明的属性
export default {
  inject: ["message"],
  mounted() {
    console.log(this.message);
  },
  // ...
};
```

### keep-alive

keep-alive 是 Vue 的内置组件，能在组件切换过程中将状态保留在内存中，防止重复渲染 DOM。

- 如果加入了 keep-alive 会多俩个生命周期 activated，deactivated
- 如果加入了 keep-alive，第一次进入组件会执行哪些生命？  
   beforeCreate -> created -> beforeMount -> mounted -> activated  
- 如果加入了 keep-alive，第二次以后进入组件只执行一个生命周期：activated

### keep-alive 实现原理 
在Vue中，keep-alive组件是用于缓存已经渲染过的组件实例的高级选项。当一个组件使用keep-alive包裹时，该组件的状态将被缓存，并在下一次渲染该组件时，直接从缓存中取出。

keep-alive组件的实现原理比较简单，其基本思路是在Vue的内部实现中维护一个缓存对象，用于存储已经渲染过的组件实例。具体而言，keep-alive组件会将其包裹的子组件封装成一个组件vnode，并将该vnode的componentInstance属性设置为子组件的实例。然后，将该vnode放入一个缓存对象中。

当下次需要渲染该组件时，keep-alive组件会检查缓存对象中是否存在已经渲染过的组件实例。如果存在，则直接将该组件实例的vnode渲染到DOM中；如果不存在，则重新渲染子组件，并将该组件实例添加到缓存对象中。

需要注意的是，由于keep-alive缓存的是组件实例，因此其仅能缓存已经渲染过的组件，而不能缓存动态生成的组件。此外，由于缓存对象是基于组件实例的，因此当组件被销毁时，其对应的缓存对象也会被清除。


### watcher是什么？
在Vue中，watcher是一种观察者模式，用于观察数据的变化。它的作用是监听数据的变化，一旦数据发生变化，就执行指定的回调函数。

- watcher主要有三种类型：computed watcher、user watcher和render watcher。
  - Computed Watcher：计算属性watcher，它是Vue中的一个特殊watcher，用于实现计算属性。当计算属性所依赖的数据发生变化时，computed watcher会重新计算计算属性的值，并通知组件重新渲染。
  - User Watcher：用户watcher，它是由用户手动创建的watcher，用于监听指定的数据变化。当指定的数据发生变化时，user watcher会执行回调函数。
  - Render Watcher：渲染watcher，它是Vue中内置的watcher，用于监听组件中所有数据的变化。当组件中的任意数据发生变化时，render watcher会重新执行渲染函数，从而更新组件的DOM。

需要注意的是，watcher是Vue中非常重要的一个概念，它负责监听数据的变化，并触发相应的操作，比如更新视图、调用回调函数等。在开发Vue应用程序时，我们经常会使用watcher来监听数据的变化，以实现组件的响应式更新。
### nextTick

- 为什么要使用 nextTick？
  - Vue 实现响应式并不是数据发生变化之后 DOM 立即变化，而是按一定的策略进行 DOM 的更新。Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化，Vue 将开启一个队列，并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发，只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后，在下一个的事件循环“tick”中，Vue 刷新队列并执行实际 (已去重的) 工作。所以为了在数据变化之后等待 Vue 完成更新 DOM，可以在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数将在 DOM 更新完成后被调用。
- 使用场景
  - 在你更新完数据后，需要及时操作渲染好的 DOM 时

### scoped

- scoped 作用：让样式在本组件中生效，不影响其他组件。
- scoped 原理：给节点新增自定义属性，然后 css 根据属性选择器添加样式。 在创建组件的时候，会给组件生成唯一的 id 值，当 style 标签给 scoped 属性时，会给组件的 html 节点都加上这个 id 值标识，如 data-v4d5aa038，然后样式表会根据这 id 值标识去匹配样式，从而实现样式隔离

- Vue 中如何做样式穿透
  语法也会有所差异。通常来说，样式穿透使用“&”符号来表示父组件，

### computed、methods、watch 有什么区别？

- computed vs methods 区别  
  computed 是有缓存的  
  methods 没有缓存
- computed vs watch 区别  
  watch 是监听，数据或者路由发生了改变才可以响应（执行）  
  computed 计算某一个属性的改变，如果某一个值改变了，计算属性会监听到进行返回  
  watch 是当前监听到数据改变了，才会执行内部代码
- computed 和 watch 的运用的场景？
  - 当我们需要进行数值计算，并且依赖于其它数据时，应该使用 computed， 因为可以利用 computed 的缓存特性，避免每次获取值时，都要重新计算；
  - 当我们需要在数据变化时执行异步或开销较大的操作时，应该使用 watch， 使用 watch 选项允许我们执行异步操作 ( 访问一个 API )，限制我们执行该操作的频率， 并在我们得到最终结果前，设置中间状态。这些都是计算属性无法做到的。

### 谈谈单页面（SPA）的理解？

SPA（ single page application ）仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。
一旦页面加载完成，SPA 不会因为用户的操作而进行页面的重新加载或跳转
而页面的变化是利用路由机制实现 HTML 内容的变换，避免页面的重新加载。

- 优点：
  - 用户体验好，内容的改变不需要重新加载整个页面，避免了不必要的跳转和重复渲染，这样相对减轻了服务器的压力
  - 前后端职责分离，架构清晰，前端进行交互逻辑，后端负责数据处理
- 缺点：
  - 初次加载耗时多
  - 不能使用浏览器的前进后退功能，由于单页应用在一个页面中显示所有的内容，所以，无法前进后退
  - 不利于搜索引擎检索：由于所有的内容都在一个页面中动态替换，所以在 SEO 上有着天然的弱势

### Class 与 Style 如何动态绑定？

Style 也可以通过对象语法和数组语法进行动态绑定
Class 可以通过对象语法和数组语法进行动态绑定

### vue 中解决跨域问题
- 使用 http-proxy-middleware 代理解决（项目使用 vue-cli 脚手架搭建）

### Vue 初始化过程中`new Vue(options)`都做了什么？

- 处理组件配置项；初始化根组件时进行了选项合并操作，将全局配置合并到根组件的局部配置上；初始化每个子组件时做了一些性能优化，将组件配置对象上的一些深层次属性放到 vm.$options 选项中，以提高代码的执行效率；
- 初始化组件实例的关系属性，比如 parent、children、root、refs 等
- 处理自定义事件
- 调用 beforeCreate 钩子函数
- 初始化组件的 inject 配置项，得到 ret[key] = val 形式的配置对象，然后对该配置对象进行响应式处理，并代理每个 key 到 vm 实例上
- 数据响应式，处理 props、methods、data、computed、watch 等选项
- 解析组件配置项上的 provide 对象，将其挂载到 vm.\_provided 属性上
- 调用 created 钩子函数
- 如果发现配置项上有 el 选项，则自动调用 $mount 方法，也就是说有了 el 选项，就不需要再手动调用 $mount 方法，反之，没提供 el 选项则必须调用 $mount
- 接下来则进入挂载阶段

### Vue3.0 里为什么要用 Proxy API 替代 defineProperty API？

- defineProperty API 的局限性最大原因是它只能针对单例属性做监听。
  - Vue2.x 中的响应式实现正是基于 defineProperty 中的 descriptor，对 data 中的属性做了遍历 + 递归，为每个属性设置了 getter、setter。这也就是为什么 Vue 只能对 data 中预定义过的属性做出响应的原因。
- Proxy API 的监听是针对一个对象的，那么对这个对象的所有操作会进入监听操作， 这就完全可以代理所有属性，将会带来很大的性能提升和更优的代码。
  - Proxy 在目标对象之前架设一层“拦截机制”，外界对该对象的访问，都必须先通过它，可以对外界的访问进行过滤和改写。
- 响应式是惰性的。
  - 在 Vue.js 2.x 中，对于一个深层属性嵌套的对象，要劫持它内部深层次的变化，就需要递归遍历这个对象，执行 Object.defineProperty 把每一层对象数据都变成响应式的，这无疑会有很大的性能消耗。
  - 在 Vue.js 3.0 中，使用 Proxy API 并不能监听到对象内部深层次的属性变化，因此它的处理方式是在 getter 中去递归响应式，这样的好处是真正访问到的内部属性才会变成响应式，简单的可以说是按需实现响应式，减少性能消耗。

### Proxy 与 Object.defineProperty 优劣对比

- Proxy 可以直接监听对象而非属性；
- Proxy 可以直接监听数组的变化；
- Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的；
- Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改；
- Proxy 作为新标准将受到浏览器厂商重点持续的性能优化，也就是传说中的新标准的性能红利；
- Object.defineProperty 的优势如下:
  兼容性好，支持 IE9，而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平，因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。

### vue 中 data 的属性可以和 methods 中方法同名吗，为什么？

- 可以同名，methods 的方法名会被 data 的属性覆盖；调试台也会出现报错信息，但是不影响执行；
- 原因：源码定义的 initState 函数内部执行的顺序：props>methods>data>computed>watch

### props 和 data 优先级谁高？

props > methods > data > computed >watch

### 你的接口请求一般放在哪个生命周期中？

- csr 中接口请求一般放在 mounted 中，也可放在 create 中。
- ssr 中不支持 mounted，需要放到 created 中

### 怎样理解 Vue 的单向数据流？

数据总是从父组件传到子组件，子组件没有权利修改父组件传过来的数据，只能请求父组件对原数据进行修改

### slot 
``` html
<slot>标签是HTML中的一个标签。在HTML5中，<slot>标签被引入作为Web组件规范的一部分。  
它允许在一个自定义元素内部插入任意内容，并通过slot元素将这些内容公开出来，从而允许外部用户自定义元素的内容。

在Vue中，<slot>标签被用来创建插槽，以便在组件中插入内容并向子组件传递内容。  
Vue会在组件中解析<slot>标签，并将插槽中的内容渲染到组件的特定位置上。  
因此，<slot>标签在Vue中扮演了一种特殊的角色。
```

- slot 插槽，可以理解为 slot 在组件模板中提前占据了位置，当复用组件时，使用相关的 slot 标签时，标签里的内容就会自动替换组件模板中对应 slot 标签的位置，作为承载分发内容的出口
- 主要作用是：复用和扩展组件，做一些定制化组件的处理

### vue 初始化页面闪动问题

原因：  
 Vue 初始化页面闪动问题是指在 Vue 单页应用（SPA）中，在页面渲染时，短暂地出现未经处理的 HTML 内容，然后再由 Vue 渲染出正确的内容，这个过程会让用户感到页面上的元素跳动、闪烁等不良体验。

解决方法:  
v-cloak指令是一个Vue内置指令，它可以在Vue应用程序加载完成之前隐藏页面的内容。具体做法是在CSS中定义一个属性选择器[v-cloak]，然后设置其display属性为none。在Vue应用程序加载完成之前，v-cloak指令会被保留在页面上，而在Vue应用程序加载完成后，v-cloak指令会被删除，从而显示页面的内容。
```html
html
<div v-cloak>
  {{ message }}
</div>

```
```css
css
[v-cloak] {
  display: none;
}

```

### setup 组合 api 的优点

- 没有 this，降低组件的耦合性，使组件复用，开发修改团队合作更加方便
- 写更加直观，接近原生 js
- 按需导入方式，节省资源

### 说说 vue 的动态组件

- 动态组件就是几个组件放在一个挂载点下，然后根据父组件的某个变量来决定显示哪个，或者都不显示。
- 在挂载点使用 component 标签，然后使用 is =“组件名”，它会自动去找匹配的组件名，如果有，则显示；

```html
<div id="app">
  <component is="one"></component>
</div>

<script>
  new Vue({
    el: "#app",
    components: {
      one: { template: "<div>我是线路一</div>" },
      two: { template: "<div>我是线路二</div>" },
      thr: { template: "<div>我是线路三</div>" },
    },
  });
</script>
```

## 指令

### v-if 和 v-show 区别

- v-if 是 false 是不渲染节点，v-show 则是都渲染节点。只是为 false 时使用 display:none 来隐藏。
- 如果 DOM 需要频繁的控制显示与隐藏时用 v-show, 否则 v-if 会更快一些。

### v-if 和 v-for 优先级

- v-for 的优先级要比 v-if 高

### v-if 和 v-for 为什么不建议一起使用？

- vue2.x 版本中，当 v-if 与 v-for 一起使用时，v-for 具有比 v-if 更高的优先级；
- vue3.x 版本中，当 v-if 与 v-for 一起使用时，v-if 具有比 v-for 更高的优先级。

官网明确指出：避免 v-if 和 v-for 一起使用，永远不要在一个元素上同时使用 v-if 和 v-for。可以先对数据在计算数据中进行过滤，然后再进行遍历渲染；
操作和实现起来都没有什么问题，页面也会正常展示。但是会带来不必要的性能消耗；

### v-html 会导致那些问题

- 可能会导致 xss 攻击，一定要保证你的内容是可以依赖的
- v-html 会替换掉标签内部的子元素
- 单页面文件里，scoped 样式不会作用在 v-html 内部，
- v-html 更新的是元素的 innerHTML 。内容按普通 HTML 插入， 不会作为 Vue 模板进行编译
- 包含的 js 不会执行，因为浏览器渲染的时候并不会渲染 js，这时要在$nextTick 中动态创建 script 标签并插入

### v-el 作用

提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标.可以是 CSS 选择器，也可以是一个 HTMLElement 实例,

### v-for 为什么要加上 key

key 的主要功能是提高 vdom 的更新速度；因为 vue 在 patch （补丁）过程中的 diff 算法对新旧节点比对时是可以通过 key 精准判断两个节点是否是同一个，从而避免频繁更新不同元素，使 patch 过程更高效。（源码位置：src/core/vdom/patch.js）
尽量避免使用 index 最为 key 值。

### vue 常用指令？

- v-model 多用于表单元素实现双向数据绑定
- v-bind：简写为：，动态绑定一些元素的属性，类型可以是：字符串、对象或数组。
- v-on:click 给标签绑定函数，可以缩写为@，例如绑定一个点击函数 函数必须写在 methods 里面
- v-for 格式： v-for="字段名 in(of) 数组 json" 循环数组或 json
- v-show 显示内容
- v-else 指令：和 v-if 指令搭配使用，没有对应的值。当 v-if 的值 false，v-else 才会被渲染出来
- v-if 指令：取值为 true/false，控制元素是否需要被渲染
- v-else-if 必须和 v-if 连用
- v-else 指令：和 v-if 指令搭配使用，没有对应的值。当 v-if 的值 false，v-else 才会被渲染出来
- v-text 解析文本
- v-html 解析 html 标签
- v-bind:class 三种绑定方法 1、对象型 '{red:isred}' 2、三元型 'isred?"red":"blue"' 3、数组型 '[{red:"isred"},{blue:"isblue"}]'
- v-once 进入页面时 只渲染一次 不在进行渲染
- v-cloak 防止闪烁
- v-pre 把标签内部的元素原位输出

### 添加自定义指令的两种方式：

- 全局指令： 通过 Vue.directive() 函数注册一个全局的指令。
- 局部指令：通过组件的 directives 属性，对该组件添加一个局部的指令。 115.你有写过自定义指令吗？自定义指令的应用场景有哪些？

### 自定义指令

自定义指令的生命周期，有 5 个事件钩子,可以设置指令在某一个事件发生时的具体行为：

- bind: 只调用一次，指令第一次绑定到元素时调用，用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
- inserted: 被绑定元素插入父节点时调用（父节点存在即可调用，不必存在于 document 中）。
- update: 被绑定元素所在的模板更新时调用，而不论绑定值是否变化。通过比较更新前后的绑定值，可以忽略不必要的模板更新（详细的钩子函数参数见下）。
- componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
- unbind: 只调用一次， 指令与元素解绑时调用。

### 指令钩子函数的参数 (包括 el，binding，vnode，oldVnode)

- el: 指令所绑定的元素，可以用来直接操作 DOM 。
- binding: 一个对象，包含以下属性：name: 指令名、value: 指令的绑定值、oldValue: 指令绑定的前一个值、expression: 绑定值的字符串形式、arg: 传给指令的参数、modifiers: 一个包含修饰符的对象。
- vnode: Vue 编译生成的虚拟节点。
- oldVnode: 上一个虚拟节点，仅在 update 和 componentUpdated 钩子中可用。

## Vue 项目性能优化

- v-if 和 v-for 区分使用场景
- computed 和 method 区分使用场景
- v-for 遍历必须为 item 添加 key，且避免同时使用 v-if
- 懒加载
- 事件的销毁
- 第三方模块按需导入
- SPA 页面采用 keep-alive 缓存组件
- 路由懒加载
- 组件的延迟加载，可以把页面资源划分为多份，用到的时候才会按需加载，这样减少第一次加载的消耗。
- 对于短时间的大量操作（缩放、滚动）使用防抖、节流函数
- 代码精简，去除 console ，可复用的方法、组件提取出来
- 不要写行内样式，避免 dom 重绘
- key 保证唯一性
- v-if 当值为 false 时内部指令不会执行,具有阻断功能，很多情况下使用 v-if 替代 v-show
- 服务端渲染 ssr

### 做过哪些 Vue 的性能优化？

- 编码阶段

  - 尽量减少 data 中的数据，data 中的数据都会增加 getter 和 setter，会收集对应的 watcher
  - v-if 和 v-for 不能连用
  - 如果需要使用 v-for 给每项元素绑定事件时使用事件代理
  - SPA 页面采用 keep-alive 缓存组件
  - 在更多的情况下，使用 v-if 替代 v-show
  - key 保证唯一
  - 使用路由懒加载、异步组件
  - 防抖、节流
  - 第三方模块按需导入
  - 长列表滚动到可视区域动态加载
  - 图片懒加载
  - SEO 优化

- 服务端渲染 SSR

  - 预渲染
  - 打包优化

- 压缩代码
  - Tree Shaking/Scope Hoisting
  - 使用 cdn 加载第三方模块
  - 多线程打包 happypack
  - splitChunks 抽离公共文件
  - sourceMap 优化

### SPA 首屏加载速度慢的怎么解决？

首屏时间（First Contentful Paint），指的是浏览器从响应用户输入网址地址，到首屏内容渲染完成的时间，此时整个网页不一定要全部渲染完成，但需要展示当前视窗需要的内容；

- 加载慢的原因：
  - 网络延时问题
  - 资源文件体积是否过大
  - 资源是否重复发送请求去加载了
  - 加载脚本的时候，渲染内容堵塞了
- 常见的几种 SPA 首屏优化方式
  - 减小入口文件积
  - 静态资源本地缓存
  - UI 框架按需加载
  - 图片资源的压缩
  - 组件重复打包
  - 开启 GZip 压缩
  - 使用 SSR

### 如何解决数据层级结构太深的问题

在开发业务时，经常会岀现异步获取数据的情况，有时数据层次比较深，如以下代码: `<span 'v-text="a.b.c.d"></span>`
可以使用 vm.$set手动定义一层数据: vm.$set("demo"，a.b.c.d)

### data 里面数据量比较大如何优化

vue 把 data、props、store 等数据做成响应式，也就是会对这些响应式数据做深度监听，给每一个 object 类型的 key（包括嵌套 object）添加 observer（vue3 使用 proxy）。所以如果我们不需要数据是响应式的，可以在.vue 文件头部直接使用 let、const 定义变量，在组件销毁的时候将该这些变量设为 null。

## 虚拟 Dom

### VNode 是什么？虚拟 DOM 是什么？

Vue 在页面上渲染的节点，及其子节点称为“虚拟节点 (Virtual Node)”，简写为“VNode”。“虚拟 DOM”是由 Vue 组件树建立起来的整个 VNode 树的称呼。

### 用 index 作为 key 可能会引发的问题

若对数据进行：逆序添加/逆序删除等破坏顺序的操作，会产生没有必要的真实 DOM 更新，界面效果虽然没有问题，但是数据过多的话，会效率过低；  
如果结构中还包含输入类的 DOM，会产生错误 DOM 更新，界面有问题；  
注意！如果不存在对数据的逆序操作，仅用于渲染表用于展示，使用 index 作为 key 是没有问题的。

## 原理

### Vue.use 是做什么的，原理是什么

作用：用来注册使用插件或者组件的方法。

原理：

1、检测组件是否注册，避免重复注册；  
2、处理入参，将第一个参数之后的参数归集，并在首部插入 this 上下文；  
3、第一个参数是对象就执行对象里面的 install 方法，是方法则直接执行这个方法，然后缓存；  
4、返回；

### Vue 数据双向绑定原理

- 实现 mvvm 的数据双向绑定，是采用数据劫持结合发布者-订阅者模式的方式，通过 Object.defineProperty()来给各个属性添加 setter，getter 并劫持监听，在数据变动时发布消息给订阅者，触发相应的监听回调。就必须要实现以下几点：
  - 实现一个数据监听器 Observer，能够对数据对象的所有属性进行监听，如有变动可拿到最新值并通知订阅者
  - 实现一个指令解析器 Compile，对每个元素节点的指令进行扫描和解析，根据指令模板替换数据，以及绑定相应的更新函数
  - 实现一个 Watcher，作为连接 Observer 和 Compile 的桥梁，能够订阅并收到每个属性变动的通知，执行指令绑定的相应回调函数，从而更新视图

### Vue3.x 响应式数据原理

- Vue3.x 改用 Proxy 替代 Object.defineProperty。
- 因为 Proxy 可以直接监听对象和数组的变化，并且有多达 13 种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。
- Proxy 只会代理对象的第一层，Vue3 是怎样处理这个问题的呢？
- 判断当前 Reflect.get 的返回值是否为 Object，如果是则再通过 reactive 方法做代理， 这样就实现了深度观测。
- 监测数组的时候可能触发多次 get/set，那么如何防止触发多次呢？我们可以判断 key 是否为当前被代理对象 target 自身属性，也可以判断旧值与新值是否相等，只有满足以上两个条件之一时，才有可能执行 trigger。

### Vue3.0 和 2.0 的响应式原理区别

Vue3.x 改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化，
并且有多达 13 种拦截方法。

### Vue.extend 作用和原理

Vue.extend()方法是用于创建一个可以继承Vue构造函数的子类的函数。该函数返回一个新的构造函数，该构造函数具有Vue构造函数的所有功能，可以使用相同的选项来配置其行为。通过这种方式，您可以创建自定义的Vue组件并重用Vue构造函数中的代码和功能。

例如，您可以通过Vue.extend()方法创建一个新的子类，并向其提供一个模板和一个数据对象：
```js 
const MyComponent = Vue.extend({
  template: '<div>{{ message }}</div>',
  data() {
    return {
      message: 'Hello, world!'
    }
  }
})

// 创建 MyComponent 实例并挂载到一个元素上
new MyComponent().$mount('#app')
```
在上面的例子中，我们使用Vue.extend()方法创建了一个名为MyComponent的子类，它继承了Vue构造函数。我们将template选项设置为一个简单的模板字符串，并将data选项设置为一个返回具有message属性的数据对象的函数。然后，我们创建一个MyComponent实例，并将其挂载到一个元素上。

使用Vue.extend()方法可以帮助您创建可重用的、可配置的Vue组件，而不必在每个组件中重复编写相同的代码。同时，它也允许您扩展和修改Vue构造函数的功能，以满足特定的需求。
### Vue 3 有哪些新特性？

Vue 3 是 Vue.js 的最新版本，相对于 Vue 2，Vue 3 有许多新特性，包括但不限于以下几点：

- 更快的运行速度：Vue 3 引入了新的响应式系统，能够在一些场景下比 Vue 2 更快地执行，同时也进行了一些性能优化。
- 更小的体积：Vue 3 中的 runtime-only 版本比 Vue 2 中的更小，能够更快地加载和渲染页面。
- Composition API：Vue 3 引入了 Composition API，让开发者可以更灵活地组织组件代码，同时也更易于代码复用和测试。
- Teleport：Vue 3 引入了 Teleport 组件，能够将一个组件的内容插入到另一个组件的指定位置，能够更方便地处理弹出框等组件。
- Suspense：Vue 3 中也引入了 Suspense 组件，能够更好地处理异步组件加载时的占位和错误处理等问题。
- Typescript 支持：Vue 3 对 Typescript 支持更加友好。
  总的来说，Vue 3 引入了很多新特性，能够更好地提高开发效率和提升用户体验。

#### Suspense使用方法
Suspense是Vue 3中新增的一个组件，用于处理异步组件渲染时的占位和错误处理。它可以让您在异步组件加载完成之前，渲染一些占位内容，并在异步组件加载完成后，渲染异步组件的内容。

使用Suspense组件，可以通过v-slot指令来指定占位内容和异步组件。当异步组件正在加载时，占位内容会被渲染，直到异步组件加载完成。如果异步组件加载失败，则可以渲染指定的错误信息。
```js 
<template>
  <div>
    <Suspense>
      <template v-slot:default>
        <div>Loading...</div>
      </template>
      <AsyncComponent />
    </Suspense>
  </div>
</template>

<script>
import { defineAsyncComponent } from 'vue'

const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'))
</script>
```
:::info 

在上面的示例中，我们在Suspense组件中包裹了一个异步组件AsyncComponent。我们使用v-slot指令来指定占位内容，并在异步组件加载完成后，渲染异步组件的内容。如果异步组件正在加载中，则会显示占位内容:Loading...。

需要注意的是，在使用Suspense组件时，必须将其包裹在包含template元素的组件中，否则会出现编译错误。此外，Suspense组件仅在Vue 3中可用，并且需要配合defineAsyncComponent函数使用，以便异步组件可以正确地加载和渲染。
:::
### vue 如何快速定位那个组件出现性能问题的

定位 Vue 组件出现性能问题可以采用如下方法：

- 使用 Vue Devtools 进行组件层级分析：Vue Devtools 是一款浏览器插件，可以方便地对 Vue 应用进行调试和分析。在 Components 选项卡下，可以看到所有的组件，并展开每个组件以查看其属性和状态。通过该工具可以轻松定位组件之间的关系，查看每个组件的生命周期以及检查渲染所需的时间。
- 使用 Chrome 开发者工具进行性能分析：Chrome 开发者工具是一款浏览器内置的调试工具，可以用来查找代码中的性能瓶颈。在 Performance 面板下，可以录制应用的运行过程并进行分析，从而找出哪些组件的渲染时间最长。
- 使用 Vue 的内置性能工具：Vue 3 提供了一个名为 Perf 的性能工具，可以帮助我们检测组件渲染的性能问题。可以在应用程序中引入 Perf 并调用其 start 和 stop 方法，这样就可以测量应用程序的性能，并获得详细的渲染时间统计信息。
- 手动添加性能分析代码：手动添加一些性能分析代码，比如记录每个组件的渲染时间，可以帮助我们更好地理解应用程序的性能瓶颈所在。可以使用 Vue 的 $watch 方法或者在组件中使用 console.time 和 console.timeEnd 方法来手动添加性能分析代码。
  综上所述，以上是一些常见的方法来定位 Vue 组件的性能问题，可以根据具体情况选择合适的方法。

### Vue 3 的响应式原理有哪些改变？

- Proxy 取代了 Object.defineProperty：Vue 3 中使用了 ES6 的 Proxy 代替了 Vue 2 中使用的 Object.defineProperty 实现响应式数据。Proxy 比 Object.defineProperty 功能更强大，可以直接监听整个对象，而不仅仅是对象的属性，还支持新增、删除属性等操作。
- 缓存了组件的渲染函数：Vue 3 使用了新的编译器，可以将组件的模板编译成渲染函数，并且会缓存渲染函数，避免重复编译，从而提高渲染性能。
- 懒执行数据初始化：在 Vue 3 中，当一个组件被创建时，它的数据并不会立即初始化，而是在组件首次被访问时才会初始化。这种懒执行的方式可以避免不必要的计算和内存浪费，提高组件的渲染性能。
- 支持 Tree-shaking：Vue 3 中的代码更容易进行 Tree-shaking，可以更好地实现代码的按需加载，从而提高应用的性能。
- Composition API：Vue 3 提供了一种新的 API——Composition API，可以让开发者更方便地组织组件的逻辑代码，将功能相似的代码进行组合，提高代码的可复用性和可维护性。

### Vue 2 中的 v-model 与 Vue 3 中的 v-model 有什么不同？

在 Vue 2 中，v-model 既可以实现双向绑定的语法糖，也可以自定义双向绑定的逻辑。在实现语法糖时，v-model 是一个拥有 :value 和 @input 两个属性的特殊指令，用于在组件内部绑定数据和响应数据的更新。

在 Vue 3 中，v-model 只能实现双向绑定的语法糖，自定义双向绑定的逻辑需要使用 v-bind 和 v-on 分别绑定组件属性和事件。为了支持 Composition API，Vue 3 中的 v-model 语法糖可以接受一个参数，将组件的 value 属性和 input 事件改为指定名称。

例如，使用 v-model:searchText 可以将一个搜索框组件的 value 属性和 input 事件分别绑定到 searchText 和 update:searchText 这两个自定义属性和事件上，从而实现与 v-model 类似的双向绑定效果。

### Vue 3 的 setup 函数是什么？有什么作用？

Vue 3 的 setup 函数是在组件实例创建之前被调用的一个新选项。它主要的作用是接收组件的 props，为组件的模板提供响应式的数据和方法，并在组件实例创建之前对它们进行预处理。在组件实例化之前运行，因此无法使用 this，但可以返回一个对象，该对象中的属性和方法可以在组件模板中使用。

具体而言，setup 函数主要有以下几个作用：

- 接收和处理组件的 props。
- 通过调用其他函数来计算和返回组件的响应式数据和方法，供组件模板使用。
- 使用 Vue 3 提供的新的 API，例如 reactive、ref、computed、watch 等。
  在 Vue 3 中，setup 函数是一个必须要定义的选项，它可以代替 Vue 2 中的 data、computed、methods 等选项。它使得组件的代码结构更加清晰，且由于使用了响应式的数据和方法，组件的性能也会有所提升。

### Vue 3 中的 Teleport 是什么？有什么作用？

Vue 3 中的 Teleport 是一种新的组件，它可以帮助开发者更容易地在 DOM 中实现基于组件的复杂 UI 布局。它的作用是提供一种在 DOM 中渲染组件的方式，而不需要直接将组件添加到父组件中。这在需要将某个组件的内容放到其他 DOM 节点中时非常有用，例如弹出框、对话框、下拉菜单等等。

Teleport 组件的具体实现是通过使用 Vue 3 的 Portals 功能，将组件渲染到指定的目标位置中。通过在 Teleport 中指定目标位置，Vue 3 可以自动将组件渲染到指定位置中，并自动处理好所有和父组件相关的生命周期钩子和事件监听器等问题，避免了直接操作 DOM 的繁琐和复杂性。

使用 Teleport 组件非常简单，只需要在模板中添加一个 Teleport 组件，同时通过 target 属性指定要将组件渲染到哪个 DOM 节点中即可，例如：

```html
<teleport to="#modal">
  <modal-component></modal-component>
</teleport>
```

上述代码将会将 modal-component 组件渲染到 #modal 节点中，而不是直接将组件添加到父组件的 DOM 中。

### Vue 3 的 Composition API 是什么？有什么优势？

Vue 3 的 Composition API 是一种新的 API 风格，可以更好地组织组件代码，解决 Vue 2 中一些复杂组件的代码难以维护的问题。

Composition API 具有以下优势：

- 更灵活的组件逻辑复用：Vue 2 中组件逻辑复用主要通过 mixin 和高阶组件实现，而这种方式存在一些问题，比如命名冲突和组件属性不清晰。Composition API 可以更好地解决这些问题，可以将相关逻辑封装为自定义 hook，然后在组件中使用，避免命名冲突和属性不清晰的问题。
- 更好的类型推断：Composition API 支持使用 TypeScript，能够更好地推断组件属性的类型，提高代码的可读性和可维护性。
- 更好的代码组织：Composition API 具有更好的代码组织能力，可以将逻辑代码按照功能模块组织，使代码更清晰，易于维护。
- 更好的性能：Composition API 可以通过 ref 和 reactive 实现局部更新，避免了 Vue 2 中因为数据响应式而导致的大量不必要的更新，从而提高了应用的性能。
  总之，Composition API 是 Vue 3 中非常重要的一部分，它的出现使得我们能够更好地组织和复用组件逻辑，提高代码的可维护性和可读性，同时也能够提高应用的性能。

### Vue 3 中的 watchEffect 和 watch 有什么不同？

在 Vue 3 中，watchEffect 和 watch 都是用于响应式数据的监听的方法，但是它们有一些不同点。

- watchEffect：可以监听任何响应式数据的变化，并在回调函数中执行相应的操作，比如更新组件状态或发送网络请求等。它可以自动收集响应式数据的依赖关系，并在依赖变化时重新执行回调函数。watchEffect 可以看做是 watch 的简化版，它不需要显式地指定要监听的响应式数据，而是通过执行回调函数时动态地收集依赖关系，因此更加灵活和方便。
- watch：需要显式地指定要监听的响应式数据，并在回调函数中执行相应的操作。它可以监听一个具体的响应式数据，也可以监听一个对象、数组、函数等。watch 在监听对象和数组时需要使用 deep 选项，才能深度遍历所有子属性并收集依赖关系。watch 的一个优势是可以对监听到的数据进行更细粒度的控制，比如可以设置 immediate 选项，指定在组件挂载时立即执行回调函数，也可以使用 lazy 选项，指定在第一次变化时不执行回调函数。
  总之，watchEffect 更加灵活和方便，适合对响应式数据的变化做出动态响应的场景；而 watch 更加细粒度，适合对特定响应式数据的变化做出响应的场景。

### Vue 3 中的 ref 和 reactive 有什么不同？

在 Vue 3 中，ref 和 reactive 都是用来创建响应式数据的 API，但是它们有一些不同之处：

- ref 只能用来包装一个基本类型的数据（如 string、number、boolean 等），而 reactive 可以包装一个普通的 JavaScript 对象，甚至是对象嵌套的情况。
- ref 创建的是一个包装器对象，当我们需要访问被包装的数据时，需要通过 .value 属性来访问，而 reactive 返回的就是一个响应式的对象，可以直接访问。
- 当使用 ref 创建响应式数据时，如果修改其包装的值，只会触发引用的组件更新；而使用 reactive 创建响应式数据时，如果修改其中的一个属性，那么所有引用该响应式对象的组件都会被更新。
  综上所述，ref 适合用于包装基本类型的数据，而 reactive 适合用于包装复杂的对象。此外，ref 可以通过 .value 获取和修改数据，而 reactive 直接访问即可。

### Vue 3 中的组合式 API 和 Options API 有什么区别？

Vue 3 中的组合式 API 和 Options API 是 Vue.js 组件开发中的两种不同风格。

Options API 是 Vue 2.x 中的标准写法，它将组件的数据、方法、生命周期钩子等按照选项的方式进行组织，以对象的形式定义。在 Options API 中，每个选项都有其特定的含义和用途，开发者可以按照自己的习惯将其分组或按照顺序排列。虽然 Options API 便于入门和使用，但是随着组件复杂度的增加，组件的代码会变得冗长、难以维护和扩展。

Vue 3 中引入了 Composition API，它是一种新的组件编写方式，提供了一种基于函数的 API，它的核心是使用一组功能单一、可复用的函数，而不是将所有的逻辑都放在一个组件选项中。使用 Composition API，开发者可以更加灵活和精细地组织组件代码，更加容易地复用逻辑，同时也可以更加方便地进行代码分离和测试。Composition API 的另外一个优点是它能够更好地支持 TypeScript。

因此，两种 API 的区别在于，Options API 是将数据和逻辑都集中在一起，组织方式是对象的形式；而 Composition API 则是根据逻辑的功能进行划分，组织方式是函数的形式。Composition API 可以帮助开发者更好地组织和重用代码，并且提高了代码的可读性和可维护性。
